<?php
/**
 * 请求处理类
 * 
 * @package Request
 * @author Shuky
 * @since v1.0
 * @version $Id: Request.php 89 2011-01-06 18:23:02Z shukyoo@gmail.com $
 */
namespace SF;


class Request
{
    // 自身实例，以实现单件模式
    protected static $_instance = null;
    
    // url基础路径
    protected $_baseUrl = null;
    
    // requestUri
    protected $_requestUri;

	// queryString
	protected $_queryString;
    
    // scriptUrl
    protected $_scriptUrl = null;
    
    // pathInfo
    protected $_pathInfo = null;
    
    // host
    protected $_hostInfo = null;
	
	
    /**
     * 获取实例
     * @return \SFramework\Request
     */
	public static function getInstance()
	{
		if (null === self::$_instance) {
			self::$_instance = new self();
		}
		return self::$_instance;
	}
	
	
	/**
	 * 获取Request_Uri
	 * @return string
	 */
	public function getRequestUri()
	{
		if (empty($this->_requestUri)) {
		    $requestUri = $_SERVER['REQUEST_URI'];
		    // Http proxy reqs setup request uri with scheme and host [and port] + the url path, only use url path
		    $schemeAndHost = $this->getHostInfo();
		    if (strpos($requestUri, $schemeAndHost) === 0) {
		        $requestUri = substr($requestUri, strlen($schemeAndHost));
		    }
		    $this->_requestUri = $requestUri;
		}
		return $this->_requestUri;
	}


	/**
	 * 获取Query_String
	 * @return string
	 */
	public function getQueryString()
	{
		return isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '';
	}
	
	
	/**
	 * 获取基础url路径
	 * @return string
	 */
	public function getBaseUrl()
	{
		if (null === $this->_baseUrl) {
			$this->_baseUrl = rtrim(dirname($this->getScriptUrl()), '\\/');
		}
		return $this->_baseUrl;
	}
	
	
	/**
	 * 获取url scheme
	 * @return string https|http
	 */
	public function getScheme()
	{
		return (isset($_SERVER['HTTPS']) && !strcasecmp($_SERVER['HTTPS'], 'on')) ? 'https' : 'http';
	}
	
	
	/**
	 * 获取host
	 * @return string
	 */
	public function getHostInfo()
	{
	    if ($this->_hostInfo == null) {
	        $scheme = $this->getScheme();
    		if (isset($_SERVER['HTTP_HOST'])) {
    			$hostName = $_SERVER['HTTP_HOST'];
    		} else {
    		    $hostName = $_SERVER['SERVER_NAME'];
    		    $port = $_SERVER['SERVER_PORT'];
    		    if (($scheme == 'http' && $port != 80) || ($scheme == 'https') && $port != 443) {
    		    	$hostName = $hostName .':'. $port;
    		    }
    		}
    		$this->_hostInfo = $scheme .'://'. $hostName;
	    }
	    return $this->_hostInfo;
	}
	
	
	/**
	 * 获取脚本的真实路径
	 * @return string
	 */
	public function getScriptUrl()
	{
		if (null === $this->_scriptUrl) {
		    $this->_scriptUrl = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : $_SERVER['PHP_SELF'];
		}
		return $this->_scriptUrl;
	}
	
	
	/**
	 * 获取来源页
	 */
	public function getReferer()
	{
		return empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER'];
	}


	/**
	 * 获取请求IP
	 */
	public function getIp()
	{
		if ( getenv('HTTP_CLIENT_IP') ) {
			$ip = getenv( 'HTTP_CLIENT_IP' );
		} elseif ( getenv('HTTP_X_FORWARDED_FOR') ) {
			$ip = getenv( 'HTTP_X_FORWARDED_FOR' );
		} elseif ( getenv('REMOTE_ADDR') ) {
			$ip = getenv( 'REMOTE_ADDR' );
		} else {
			$ip = $_SERVER['REMOTE_ADDR'];
		}
        return $ip;
	}
	
	
	/**
	 * 获取不带host和scriptUrl相应信息的PATH
	 * @return string
	 */
	public function getPathInfo()
	{
	    if (null === $this->_pathInfo) {
    		$requestUri = $this->getRequestUri();
    		$baseUrl = $this->getBaseUrl();
    		$scriptUrl = $this->getScriptUrl();
    		// Remove the query string from REQUEST_URI
    		if (($pos = strpos($requestUri, '?')) !== false) {
    			$requestUri = substr($requestUri, 0, $pos);
    		}
    		// 如果带有一至的scriptUrl，则去除scriptUrl
    		// 否则去除Host
    		if (strpos($requestUri, $scriptUrl) === 0) {
    			$pathInfo = substr($requestUri, strlen($scriptUrl));
    		} elseif ($baseUrl !== null) {
    		    $pathInfo = substr($requestUri, strlen($baseUrl));
    		}
    		// 去除斜杠
    		$pathInfo = trim($pathInfo, '/');
    		// 去除多重无意义的重复性斜杠
    		$this->_pathInfo = preg_replace('/\/+/', '/', $pathInfo);
	    }
	    return $this->_pathInfo;
	}
	
	
	/**
	 * 忽略get和post，获取传递的参数
	 * get优先，然后post，然后默认值
	 * 如果$key为空，则获取全部
	 * @param string $key
	 * @param string $default
	 * @return mixed
	 */
	public function getParam($key = null, $default = null)
	{
	    if (null === $key) {
	    	return $_GET + $_POST;
	    }
		return isset($_GET[$key]) ? $this->getQuery($key) : (isset($_POST[$key]) ? $this->getPost($key) : $default);
	}
	
	/**
	 * 设置参数
	 * @param string|array $key 如果是数组，则$value无用
	 * @param mixed $value
	 */
	public function setParam($key, $value = null)
	{
		if (is_string($key)) {
			$_GET[$key] = $value;
		}
		if (is_array($key)) {
			foreach ($key as $k=>$v) {
			    $_GET[$k] = $v;
			}
		}
	}
	
	
	/**
	 * 按get获取
	 * 如果没有指定键值，则全部获取
	 * @todo 如果是嵌套数组怎么处理
	 * @param string $key
	 * @param mixed $default
	 * @return mixed
	 */
	public function getQuery($key = null, $default = null)
	{
	    if (null === $key) {
	    	return $_GET;
	    }
		return isset($_GET[$key]) ? trim($_GET[$key]) : $default;
	}
	
	
    /**
     * 按post获取
     * 如果没有指定键值，则全部获取
     * @todo 如果是嵌套数组怎么处理
     * @param string $key
     * @param mixed $default
     * @param int $trim 是否需要进行trim处理
     * @return mixed
     */
    public function getPost($key = null, $default = null, $trim = 1)
    {
        if (null === $key) {
            return $_POST;
        }
        return isset($_POST[$key]) ? ($trim?trim($_POST[$key]):$_POST[$key]) : $default;
    }
    
    
    /**
     * 是否是POST请求
     */
    public function isPost()
    {
    	return ('POST' == $_SERVER['REQUEST_METHOD']);
    }
}

/**
 * 路由
 * 
 * @package SF
 * @author Shuky
 * @since v1.0
 * @version $Id: Router.php 95 2011-01-11 18:30:09Z shukyoo@gmail.com $
 */
namespace SF;

class Router
{
    /**
     * @var Request
     */
    protected $_request = null;
    
    // 自身单件实例类
    protected static $_instance = null;
    
    // 控制
    protected $_controller = 'index';
    
    // 动作
    protected $_action = 'index';

	// 默认首要参数索引
	protected $_index = '';
    
    
    /**
     * 自身单件实例获取
     */
    public static function getInstance()
    {
    	if (null === self::$_instance) {
    		self::$_instance = new self();
    	}
    	return self::$_instance;
    }
    
    
    /**
     * 构造，按/controller/action/index方式解析请求
     * @todo 注意一下这里跟parseLink的纠结，如何去统一提取规则
     */
    protected function __construct()
    {
    	$path = $this->getRequest()->getPathInfo();
		$path = explode('/', $path);
		if (!empty($path[0])) {
			$this->_controller = trim(strtolower($path[0]));
		}
		if (!empty($path[1])) {
			$this->_action = trim(strtolower($path[1]));
		}
		if (isset($path[2])) {
			$this->_index = trim($path[2]);
		}
    }
    
    
    /**
     * 按规则处理路径串
     * 路径格式：controller/action/index
     * 默认以此格式作为链接样式
     * 返回绝对Url路径
     */
    public function parseLink($path)
    {
		if (strpos($path, './') === 0) {
			$path = substr($path, 2);
			$path = $this->getController() .'/'. $path;
		}
        if (strpos($path, '/') === 0) {
        	$path = substr($path, 1);
        }
    	return $this->getRequest()->getBaseUrl() .'/'. $path;
    }
    
    
    /**
     * 获取请求
     * @return Request
     */
    public function getRequest()
    {
        if (null === $this->_request) {
    		$this->_request = Request::getInstance();
    	}
    	return $this->_request;
    }
    
    
    /**
     * 路由解析和导向
     */
    public function dispatch()
    {
	    $controlClass = 'APP\\C\\'. ucfirst($this->_controller);
	    if (!class_exists($controlClass)) {
	    	$this->notFound();
	    }
		$controlClass = new $controlClass();
		$action = $this->_action .'Action';
		if (!method_exists($controlClass, $action)) {
			$this->notFound();
		}
		$controlClass->$action();
    }
    
    
    /**
     * 404导向
     */
    public function notFound($toUrl = '/home/notfound')
    {
    	$this->redirect($toUrl);
    }
    
    
    /**
     * 转向
     */
	public function redirect($toUrl)
	{
	    $toUrl = (strpos($toUrl, 'http://') === false) ? $this->parseLink($toUrl) : $toUrl;
		header('Location: '. $toUrl);
        exit;
	}


	/**
	 * 生成链接
	 */
	public function genQuery($path='', $params = array(), $append = true)
	{
        // 基础
        $base = $this->getRequest()->getBaseUrl();
        if (strchr($base, '/') != '/') {
            $base .= '/';
        }
        // 组织path
        $path = ltrim($path, '/');
        if ($append) {
            $path = $this->getRequest()->getPathInfo() . '/' . $path;
        }
        $path = trim($path, '/');
        // 组织参数
        $qstr = $append ? $this->getRequest()->getQueryString() : '';
        if ($params) {
            $aq = array();
            foreach ($params as $k=>$v) {
                if ($v !== '') {
                    $aq[] = $k .'='. $v;
                }
            }
            if (!empty($aq)) {
                $qstr .= (($qstr ? '&' : '') . implode('&', $aq));
            }
        }
        if ($qstr) {
            $qstr = '?'. $qstr;
        }
        return $base . $path . $qstr;
	}

	
	/**
	 * 获取Controller
	 */
	public function getController()
	{
		return $this->_controller;
	}
	
	
	/**
	 * 获取和设置Action
	 */
	public function getAction()
	{
		return $this->_action;
	}
	public function setAction($action)
	{
		$this->_action = $action;
	}


	/**
	 * 获取和设置Index
	 */
	public function getIndex()
	{
		return $this->_index;
	}
	public function setIndex($index)
	{
		$this->_index = $index;
	}
}
/**
 * 全局共享寄存器
 * 提升程序效率，降低类耦合
 *
 * @package Request
 * @author Shuky
 * @since v1.0
 * @version $Id: Registry.php 82 2010-12-23 18:23:35Z shukyoo@gmail.com $
 */
namespace SF;

class Registry extends \ArrayObject
{
	protected static $_registry = null;
	protected static $_register = null;

	public function __construct()
	{
		parent::__construct(array(), parent::ARRAY_AS_PROPS);
	}


	public static function getInstance()
	{
		if (null === self::$_registry) {
			self::$_registry = new self();
		}
		return self::$_registry;
	}


	/**
	 * 设置一个默认装配类
	 */
	public static function setRegister($register)
	{
		self::$_register = $register;
	}


	/**
	 * 注册
	 */
	public static function set($index, $value)
	{
		$instance = self::getInstance();
		$instance->offsetSet($index, $value);
	}


	/**
	 * 获取
	 */
	public static function get($index)
	{
		$instance = self::getInstance();
		if (!$instance->offsetExists($index)) {
			// 如果不存在，则尝试用默认装配类加载
			$register = self::$_register;
			$method = 'get'. ucfirst(strtolower($index));
			if ($register && method_exists($register, $method)) {
				self::set($index, call_user_func(array($register, $method)));
			} else {
				throw new Exception('索引项`'. $index .'`不存在');
			}
		}
		return $instance->offsetGet($index);
	}


	public static function isRegistered($index)
	{
		if (null === self::$_registry) {
			return false;
		}
		return self::$_registry->offsetExists($index);
	}
}

/**
 * @package SF
 * @author Shuky
 * @since v1.0
 * @version $Id: Exception.php 98 2011-03-24 15:46:36Z shukyoo@gmail.com $
 */
namespace SF;

class Exception extends \Exception
{
    public function __construct($msg = '', $code = 0, $previous = null)
    {
        parent::__construct($msg, $code, $previous);
        echo '<h1>Exception:</h1>';
        echo $this->getMessage() .'<br /><br />';
        echo $this->getFile() .' 行：'. $this->getLine();
        exit;
    }
}